﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Muel.TypeScriptFacade.Core;
using Muel.TypeScriptFacade.Core.Configuration;

namespace Muel.TypeScriptFacade.ConsoleApplication
{
    class Program
    {
        private enum ConsoleParameter
        {
            None,
            Assemblies,
            ServiceUrl,
            OutputPath,
            Settings,
        }

        static void Main(string[] args)
        {
            List<string> parameterErrors = new List<string>();
            List<string> assemblies = new List<string>();
            string serviceUrl = null;
            string outputPath = null;
            string settingsPath = null;

            if (args.Length == 0)
            {
                return;
            }

            ConsoleParameter currentParameter = ConsoleParameter.None;
            for (var i = 0; i < args.Length; i++)
            {
                string arg = args[i];
                if (arg.StartsWith("-"))
                {
                    string parameterName = arg.Substring(1, arg.Length - 1);
                    switch (parameterName)
                    {
                        case "Settings":
                            currentParameter = ConsoleParameter.Settings;
                            break;
                        case "Assemblies":
                            currentParameter = ConsoleParameter.Assemblies;
                            break;
                        case "ServiceUrl":
                            currentParameter = ConsoleParameter.ServiceUrl;
                            break;
                        case "OutputPath":
                            currentParameter = ConsoleParameter.OutputPath;
                            break;
                        default:
                            parameterErrors.Add(string.Format("Unexpected parameter name {0}", parameterName));
                            break;
                    }
                }
                else
                {
                    switch (currentParameter)
                    {
                        case ConsoleParameter.None:
                            parameterErrors.Add(string.Format("No parameter specified for argument {0}", arg));
                            break;
                        case ConsoleParameter.Assemblies:
                            assemblies.Add(arg);
                            break;
                        case ConsoleParameter.Settings:
                            settingsPath = arg;
                            if (!File.Exists(settingsPath))
                            {
                                parameterErrors.Add("Parameter -Settings contains a file which could not be found");
                            }
                            break;
                        case ConsoleParameter.ServiceUrl:
                            if (!string.IsNullOrEmpty(serviceUrl))
                            {
                                parameterErrors.Add("Parameter -ServiceUrl has already been specified");
                            }
                            else
                            {
                                serviceUrl = arg;
                            }
                            break;
                        case ConsoleParameter.OutputPath:
                            if (!string.IsNullOrEmpty(outputPath))
                            {
                                parameterErrors.Add("Parameter -OutputPath has already been specified");
                            }
                            else
                            {
                                outputPath = arg;
                            }
                            break;
                        default:
                            throw new Exception("Unrecognised parameter type");
                    }
                }
            }

            if (!assemblies.Any())
            {
                parameterErrors.Add("-Assemblies is a required parameter");
            }
            if (string.IsNullOrEmpty(outputPath))
            {
                parameterErrors.Add("-OutputPath is a required parameter");
            }

            TypeScriptFacadeSettings settings = null;
            if (!string.IsNullOrEmpty(settingsPath))
            {
                settings = TypeScriptFacadeSettings.LoadFromFile(settingsPath);
            }

            if (parameterErrors.Any())
            {
                DisplayErrors(parameterErrors);
            }
            else
            {
                try
                {
                    TypeScriptServiceManager manager = new TypeScriptServiceManager(settings);
                    if (!string.IsNullOrEmpty(serviceUrl))
                    {
                        manager.Settings.DefaultDomainRelativeServiceUrl = serviceUrl;
                    }
                    foreach (string assemblyPath in assemblies)
                    {
                        Assembly assembly = Assembly.LoadFile(assemblyPath);
                        manager.Load(assembly);
                    }
                    string typescript = manager.ToTypeScript();

                    File.WriteAllText(outputPath, typescript);
                }
                catch (Exception ex)
                {
                    parameterErrors.Add(string.Format("Exception: {0}", ex.Message));
                    DisplayErrors(parameterErrors);
                }
            }
        }

        private static void DisplayErrors(List<string> parameterErrors)
        {
            Console.WriteLine("The following errors occurred:");
            Console.WriteLine();
            foreach (string error in parameterErrors)
            {
                Console.WriteLine(string.Format(" * {0}", error));
            }
            Console.WriteLine();
            Console.WriteLine("Please use the following parameters:");
            Console.WriteLine();
            Console.WriteLine(" -Assemblies [assembly1] [assembly2] ...");
            Console.WriteLine("    (a space-separated list of paths to .NET assemblies containing the relevant services)");
            Console.WriteLine();
            Console.WriteLine(" -Settings [pathToTsfFile]");
            Console.WriteLine("    (the path to find the .tsf file containing the code generation settings)");
            Console.WriteLine();
            Console.WriteLine(" -OutputPath [outputpath]");
            Console.WriteLine("    (the path to output the generated TypeScript file)");
            Console.WriteLine();
            Console.WriteLine(" [-ServiceUrl [domainrelativeurl]]");
            Console.WriteLine("    (OPTIONAL: the path from the domain to the container for the services, e.g. Services/)");
        }
    }
}